﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FB.DataAccess;
using FB.Utilities;
using log4net;

namespace FB.Bussiness
{
    public class VideoBO
    {
        private static readonly ILog log = LogManager.GetLogger("VideoBO");
        public IEnumerable<Video> GetAllVideo()
        {
            VideoDAO _dao = new VideoDAO();
            return _dao.GetAllVideo();
        }
        public Video GetVideoById(Guid id)
        {
            VideoDAO _dao = new VideoDAO();
            return _dao.GetVideoByID(id);
        }
        public bool UpdateVideo(Video inVideo)
        {
            VideoDAO _dao = new VideoDAO();
            return _dao.UpdateVideo(inVideo);
        }
        public ReturnValue<bool> InsertVideo(Video inVideo)
        {
            VideoDAO _dao = new VideoDAO();
            return _dao.InsertVideo(inVideo);
        }

        public ReturnValue<Video> InsertVideoAndReturn(Video inVideo)
        {
            VideoDAO _dao = new VideoDAO();
            return _dao.InsertVideoAndReturn(inVideo);
        }

        public ReturnValue<bool> DelVideo(Guid id)
        {
            //Not really delete -- We only mark it is delelte
            VideoDAO _dao = new VideoDAO();
            Video CurrentVideo = _dao.GetVideoByID(id);
            CurrentVideo.IsDelete = true;
            bool result = _dao.UpdateVideo(CurrentVideo);
            return new ReturnValue<bool>(result, "");
        }

        public ReturnValue<int> FeedVideoFromPlaylist(Guid playlistID)
        {
            // Get channel name
            Video PlayList = GetVideoById(playlistID);
            if (PlayList == null)
            {
                return new ReturnValue<int>(0, "Playlist is not exist in DB");
            }
            //Number of video are feed
            int VideoFeedNo = 0;
            // Get list video
            List<YouTubeVideoObject> VideoList = YouTubeVideoHelper.GetVideosFromPlaylist(PlayList.YoutubeID);

            // Insert list video to db
            VideoBO VBO = new VideoBO();
            foreach (YouTubeVideoObject v in VideoList)
            {
                if (VBO.InsertVideo(new Video(v)).RetValue)
                {
                    VideoFeedNo++;
                }
            }

            return new ReturnValue<int>(VideoFeedNo, "");
        }

        public YouTubeVideoObject FeedVideoByYoutubeID(string youtubeVideoID)
        {
            // Get list video
            YouTubeVideoObject FeedVideo = YouTubeVideoHelper.GetVideosByYoutubeID(youtubeVideoID);
            return FeedVideo;
        }
        public IEnumerable<View_VideoInCategory> GetVideoByCategory(Guid catID, int limit)
        {
            VideoDAO _dao = new VideoDAO();
            return _dao.GetVideoByCategory(catID, limit);
        }

        public IEnumerable<Video> GetAllPlaylist()
        {
            VideoDAO _dao = new VideoDAO();
            return _dao.GetAllPlaylist();
        }

        public void DelPlaylist(Guid playlistID)
        {
            DelVideo(playlistID);
        }

        public void IncreaseViewCount(string itemID)
        {
            VideoDAO _dao = new VideoDAO();
            _dao.IncreaseViewCount(itemID);
        }

        public Video GetVideoByYouTubeID(string videoYoutubeID)
        {
            VideoDAO _dao = new VideoDAO();
            return _dao.GetVideoByYoutubeID(videoYoutubeID);
        }

        public List<Video> GetRelatedVideo(string videoYoutubeID, int limit)
        {
            // Get related video by 3 part:
            // 1. By video Title and Video Description
            // 2. By video Tag
            // 3. By video category
            // The rate for 3 part is: 3 - 2 - 1
            double Weight = limit/5;
            int LimitByVideo = Convert.ToInt32(Math.Round(Weight*3));
            int LimitByTag = Convert.ToInt32(Math.Round(Weight*2));
            int LimitByCate = limit - LimitByVideo - LimitByTag;
            List<Guid> AlreadyGetList = new List<Guid>();
            VideoDAO VDAO = new VideoDAO();

            Video CurrentVideo = VDAO.GetVideoByYoutubeID(videoYoutubeID);
            AlreadyGetList.Add(CurrentVideo.ID);

            //1. Get related video by infomation of title and description
            List<Video> ResultByVideo = VDAO.GetRelatedVideoByVideo(videoYoutubeID, LimitByVideo, AlreadyGetList);
            int CountByVideoResult = ResultByVideo.Count;
            if (LimitByVideo != CountByVideoResult)
            {
                LimitByTag += (LimitByVideo - CountByVideoResult);
            }

            foreach (Video item in ResultByVideo)
            {
                AlreadyGetList.Add(item.ID);
            }

            //2. Get related video by information of tag
            List<Video> ResultByTag = GetRelatedVideoByTagOfVideo(videoYoutubeID, LimitByTag, AlreadyGetList);
            int CountByTagResult = Common.CountIEnumberable(ResultByTag);
            
            foreach (Video item in ResultByTag)
            {
                AlreadyGetList.Add(item.ID);
            }


            //3. Get related video by information of category
            LimitByCate = limit - CountByVideoResult - CountByTagResult;
            List<Video> ResultByCategory = GetRelatedVideoByCategoryOfVideo(videoYoutubeID, LimitByCate, AlreadyGetList); 

            // Combine these result
            List<Video> FinalResult = new List<Video>();
            FinalResult.AddRange(ResultByVideo);
            FinalResult.AddRange(ResultByTag);
            FinalResult.AddRange(ResultByCategory);
            return FinalResult;
        }

        private List<Video> GetRelatedVideoByCategoryOfVideo(string videoYoutubeID, int LimitByCate, List<Guid> exceptVideoList)
        {
            List<Video> Result = new List<Video>();
            List<Guid> ExeptList = new List<Guid>();
            ExeptList.AddRange(exceptVideoList);
            VideoDAO VDAO = new VideoDAO();
            VideoCategoryDAO CDAO = new VideoCategoryDAO();
            //Get list tag of this video
            List<VideoCategory> CateList = CDAO.GetVideoCateByVideoYoutubeID (videoYoutubeID);
            if (CateList != null && CateList.Count > 0)
            {
                //Get video by using these tag
                int NumOfCat = CateList.Count;
                for (int i = 1; i < NumOfCat; i++)
                {
                    List<Video> TempList = VDAO.GetVideoByCateId(CateList.ElementAt(i).ID, LimitByCate / NumOfCat, ExeptList);
                    Result.AddRange(TempList);
                    foreach (Video item in TempList)
                    {
                        ExeptList.Add(item.ID);
                    }
                }
                //Get for first tag
                int LimitFinalGet = LimitByCate - ((LimitByCate / NumOfCat) * (NumOfCat - 1));
                Result.AddRange(VDAO.GetVideoByCateId(CateList.ElementAt(0).ID, LimitFinalGet, ExeptList));
            }
            return Result;
        }

        private List<Video> GetRelatedVideoByTagOfVideo(string videoYoutubeID, int limitByTag, List<Guid>exceptVideoList)
        {
            List<Video> Result = new List<Video>();
            List<Guid> ExeptList = new List<Guid>();
            ExeptList.AddRange(exceptVideoList);
            VideoDAO VDAO = new VideoDAO();
            TagDAO TDAO = new TagDAO();
            //Get list tag of this video
            List<Tag> TagList = TDAO.GetTagByVideoYoutubeID(videoYoutubeID);
            if (TagList != null && TagList.Count > 0 )
            {
                //Get video by using these tag
                int NumOfTag = TagList.Count;
                for (int i = 1; i < NumOfTag; i++)
                {
                    List<Video> TempList = VDAO.GetVideoByTagId(TagList.ElementAt(i).ID, limitByTag / NumOfTag, ExeptList);
                    Result.AddRange(TempList);
                    foreach (Video item in TempList)
                    {
                        ExeptList.Add(item.ID);
                    }
                }
                //Get for first tag
                int LimitFinalGet = limitByTag - ((limitByTag / NumOfTag) * (NumOfTag - 1));
                Result.AddRange(VDAO.GetVideoByTagId(TagList.ElementAt(0).ID, LimitFinalGet, ExeptList));
            }
            return Result;
        }

        public string GetTagStringOfVideo(Guid videoID)
        {
            VideoDAO _dao = new VideoDAO();
            List<View_VideoTag> TagList = _dao.GetTagByVideoID(videoID);
            StringBuilder sb = new StringBuilder();
            foreach (View_VideoTag item in TagList)
            {
                sb.Append(item.TagName);
                sb.Append(";");
            }
            return sb.ToString();
        }

        public List<Video> SearchVideo(string keyword, int limit)
        {
            // Search video by 2 part:
            // 1. Search in Video table
            // 2. Search in tag table
            
            // The rate for 2 part is: 4 - 1
            int LimitInVideo = (limit / 5)*4;
            int LimitInTag = limit - LimitInVideo;

            // Search by video and search by tag
            List<Video> Result = new List<Video>();
            List<Guid> ExeptList = new List<Guid>();
            VideoDAO VDAO = new VideoDAO();
            TagDAO TDAO = new TagDAO();


            //Search in video
            List<Video> ResultByVideo = (VDAO.SearchVideo(keyword, LimitInVideo));
            Result.AddRange(ResultByVideo);
            foreach (Video item in ResultByVideo)
            {
                ExeptList.Add(item.ID);
            }
            LimitInTag = limit - ResultByVideo.Count;
            

            //Get video by tag
            List<Video> ResultByTag = SearchVideoByTag(keyword, LimitInTag, ExeptList);
            Result.AddRange(ResultByTag);

            return Result;
        }

        private List<Video> SearchVideoByTag(string keyword, int LimitInTag, List<Guid> exceptVideoList)
        {
            List<Video> Result = new List<Video>();
            List<Guid> ExeptList = new List<Guid>();
            ExeptList.AddRange(exceptVideoList);
            VideoDAO VDAO = new VideoDAO();
            TagDAO TDAO = new TagDAO();
            //Get list tag of this video
            List<Tag> TagList = TDAO.SearchTag(keyword, 10);
            if (TagList != null && TagList.Count >0)
            {
                //Get video by using these tag
                int NumOfTag = TagList.Count;
                for (int i = 1; i < NumOfTag; i++)
                {
                    List<Video> TempList = VDAO.GetVideoByTagId(TagList.ElementAt(i).ID, LimitInTag / NumOfTag, ExeptList);
                    Result.AddRange(TempList);
                    foreach (Video item in TempList)
                    {
                        ExeptList.Add(item.ID);
                    }
                }
                //Get for first tag
                int LimitFinalGet = LimitInTag - ((LimitInTag / NumOfTag) * (NumOfTag - 1));
                Result.AddRange(VDAO.GetVideoByTagId(TagList.ElementAt(0).ID, LimitFinalGet, ExeptList));
            }
            return Result;
        }

        public Guid? GetOne2Edit()
        {
            VideoDAO VDAO = new VideoDAO();
            return VDAO.GetOneToEdit();
        }

        public void DoCheckVideoJob()
        {
            log.Info("IV. Get video to check");
            //Get one chanel which have last feed more than 15 days to feed
            VideoDAO VDAO = new VideoDAO();
            Video cVideo = VDAO.GetVideo2CheckAlive();
            if (cVideo != null)
            {
                YouTubeVideoObject FeedVideo = FeedVideoByYoutubeID(cVideo.YoutubeID);
                if (FeedVideo == null)
                {
                    cVideo.IsDead = true;
                    UpdateVideo(cVideo);
                    log.Info("V. Set video " + cVideo.YoutubeID + " is deaded");
                }
            }
            else
            {
                log.Info("V. Cant get video to check alive");
            }
        }
    }
}
